import java.lang.*;
import java.util.*;
import java.io.*;
import org.apache.commons.collections.*;

public class GetDependency 
{
	public void getdependency(String obj2ObjMapFile, String proj2ProjMapFile,
			String remoteObj2ProjMapFile, String localObj2ProjMapFile, String basedFreMapFile) 
		throws IOException
	{
		MultiHashMap remoteObj2ProjMap = new MultiHashMap();
		Utilities.readMultiMapFromFile(remoteObj2ProjMap, remoteObj2ProjMapFile);
		
		MultiHashMap obj2ObjMap = new MultiHashMap();
		Utilities.readMultiMapFromFile(obj2ObjMap, obj2ObjMapFile);
		
		//MultiHashMap obj2ProjMap = new MultiHashMap();
		//Utilities.readMultiMapFromFile(obj2ProjMap, obj2ProjMapFile);
		
		HashMap<String, String> localObj2ProjMap = new HashMap<String, String>();
		Utilities.readMapFromFile(localObj2ProjMap, localObj2ProjMapFile);
		
		MultiHashMap proj2ProjMap = new MultiHashMap();
		
		//get two maps, remote-obj vs obj, remote-obj vs remote-obj
		MultiHashMap remoteObj2ObjMap = new MultiHashMap();
		MultiHashMap remoteObj2RemoteObjMap = new MultiHashMap();
		
		MultiHashMap fre = new MultiHashMap();
		
		
		for (String obj: (Set<String>) obj2ObjMap.keySet())
		{
			//study only remote-obj
			if (Utilities.isOutObj(obj) == false)
			{
				continue;
			}
			
			for (String basedObj: (Collection<String>) obj2ObjMap.get(obj))
			{
				//study only remote-obj vs remote-obj
				if (Utilities.isOutObj(basedObj) == true)
				{
					remoteObj2RemoteObjMap.put(obj, basedObj);
				}
				
				//study remote-obj vs obj
				remoteObj2ObjMap.put(obj, basedObj);
			}
		}
		
		
		HashSet<String> remoteObjSet = new HashSet<String>();
		HashSet<String> localTopObjSet = new HashSet<String>();
		HashSet<String> localBottomObjSet = new HashSet<String>();
		
		int unResolvedSize = -1;
		while(unResolvedSize != proj2ProjMap.totalSize())
		{
			//System.out.println(proj2ProjMap.size());
			//System.out.println(remoteObj2ObjMap.size());
			
			unResolvedSize = proj2ProjMap.totalSize();

			
			Set<String> objSet = remoteObj2ObjMap.keySet();
			for (String obj: objSet)
			{
				remoteObjSet.clear();
				localTopObjSet.clear();
				localBottomObjSet.clear();
				
				boolean unknownObj = false;
				Collection<String> objColl =  (Collection<String>) remoteObj2ObjMap.get(obj);
				for (String basedObj: objColl)
				{
					/*
					if (obj.equals("t94336_out_host_common_obj_JAVA_LIBRARIES_core_tests_hostdex_intermediates_classes_full_debug_jar"))
					{
						//System.out.println(basedObj);
					}
					*/
						
					//an identified out-based-obj
					if (Utilities.isOutObj(basedObj) == true 
							&& remoteObj2ProjMap.containsKey(basedObj))
					{
						remoteObjSet.add(basedObj);
					}
						// identified local-based top obj
						else if (Utilities.isOutObj(basedObj) == false
							&& Utilities.isTop(basedObj) == true
							&& localObj2ProjMap.containsKey(basedObj) == true)
						{
							localTopObjSet.add(basedObj);
						}
						//// identified local-based bottom obj
						else if (Utilities.isOutObj(basedObj) == false
								&& Utilities.isDependency(basedObj) == false
								&& localObj2ProjMap.containsKey(basedObj) == true)
						{
							localBottomObjSet.add(basedObj);
						}
						else if(Utilities.isOutObj(basedObj) == false
								&& localObj2ProjMap.containsKey(basedObj) == false)
						{
							unknownObj = true;
							
							break;
						}
					
				}
				
				if (unknownObj == true)
				{
					continue;
				}
				
				//only study case when remote-based-obj and local-based-obj both exist
				if ((remoteObjSet.size() <= 0) || (localTopObjSet.size() <= 0))
				{
					continue;
				}
				
				//only study all the bottom remote-obj
				boolean remoteAllAreBottom = true;
				for (String remoteObj: remoteObjSet)
				{
					if (Utilities.isDependency(remoteObj) == false)
					{
						remoteAllAreBottom = false;
						
						break;
					}
				}
				
				if (remoteAllAreBottom == false)
				{
					continue;
				}
				
				
				
				//System.out.println(proj2ProjMap.totalSize());
				
				HashSet<String> localTopProjSet = new HashSet<String>();
				for (String localObj: localTopObjSet)
				{
					localTopProjSet.add(localObj2ProjMap.get(localObj));			
				}
				
				if (localTopProjSet.size() != 1)
				{
					continue;
				}
				
				HashSet<String> localBottomProjSet = new HashSet<String>();
				for (String localObj: localBottomObjSet)
				{
					localTopProjSet.add(localObj2ProjMap.get(localObj));			
				}
				
				if (localTopProjSet.size() != 1)
				{
					continue;
				}
				
				for (String localTopObj: localTopObjSet)
				{
					String localTopProj = localObj2ProjMap.get(localTopObj);
					
					//localTopProj -> remoteProj
					for (String remoteObj: remoteObjSet)
					{
						
						for (String remoteProj: 
							(Collection<String>) remoteObj2ProjMap.get(remoteObj))
						{
							Collection<String> coll = 
								(Collection<String>)proj2ProjMap.get(localTopProj);
							
							if (coll == null || coll.contains(remoteProj) == false)
							{
								if (localTopProj.equals(remoteProj) == false)
								{
									proj2ProjMap.put(localTopProj, remoteProj);
									//System.out.println(localTopProj + " : " + remoteProj);
									if (remoteObj2ProjMap.containsValue(obj, localTopProj) == false)
									{
										remoteObj2ProjMap.put(obj, localTopProj);
									}
									
									/*
									if(proj.equals("platform/frameworks/ex") || basedProj.equals("platform/frameworks/base"))
									{
										System.out.println("----------");
										System.out.println(obj + " : " + obj + " based on " + basedObj);
										System.out.println(proj + " based on " + basedProj);
									}
									*/
									
									/*
									if(localTopProj.equals("platform/frameworks/base") && remoteProj.equals("platform/frameworks/ex"))
									{
										System.out.println("----------");
										System.out.println(obj + " : " + localTopObj + " based on " + remoteObj);
										System.out.println(localTopProj + " based on " + remoteProj);
									}
									*/
									
									String projPair = localTopProj  + " , " + remoteProj;
									fre.put(projPair, 1);
								}
								
							}
							
							else if (coll == null && coll.contains(remoteProj) == true)
							{
								if (localTopProj.equals(remoteProj) == false)
								{
									String projPair = localTopProj  + " , " + remoteProj;
									fre.put(projPair, 1);
								}
							}
							
						}
						
					}
					
					
					/*
					//if there exists bottom obj set
					if (localBottomProjSet.size() <= 0)
					{
						continue;
					}
					
					//System.out.println(proj2ProjMap.totalSize());
					
					for (String localBottomProj: localBottomProjSet)
					{
						Collection<String> coll = 
							(Collection<String>)proj2ProjMap.get(localTopProj);
						
						//String localBottomProj = localObj2ProjMap.get(localBottomObj);
						
						if (coll == null || coll.contains(localBottomProj) == false)
						{
							if (localTopProj.equals(localBottomProj) == false)
							{
								proj2ProjMap.put(localTopProj, localBottomProj);
								
								String projPair = localTopProj  + " , " + localBottomProj;
								fre.put(projPair, 1);
							}
						}
						else if (coll != null && coll.contains(localBottomProj) == true)
						{
							if (localTopProj.equals(localBottomProj) == false)
							{
								String projPair = localTopProj  + " , " + localBottomProj;
								fre.put(projPair, 1);
							}
						}
	
					}
					
					*/
				}
				//System.out.println("-dsfasdfasdfasd");
			
			}
				
		}

		System.out.println(proj2ProjMap.size());
		
		

		
		
		HashSet<String> projSet = new HashSet<String>();
		for (String proj: (Set<String>) proj2ProjMap.keySet())
		{
			projSet.add(proj);
		}
		
		for (String basedProj: (Collection<String>)proj2ProjMap.values())
		{
			projSet.add(basedProj);
		}
		
		System.out.println(projSet.size());
		
		
		unResolvedSize = -1;
		while(unResolvedSize != proj2ProjMap.totalSize())
		{	
			unResolvedSize = proj2ProjMap.totalSize();
			
			for (String obj: (Set<String>) remoteObj2ObjMap.keySet())
			{
				if (Utilities.isOutObj(obj) == false)
				{
					continue;
				}
				
				Collection<String> basedRemoteObjColl = (Collection<String>) remoteObj2ObjMap.get(obj);
				if (basedRemoteObjColl == null)
				{
					continue;
				}
				
				//study only remote based obj
				boolean allAreRemote = true;
				
				for (String basedObj: basedRemoteObjColl)
				{
					if (Utilities.isOutObj(basedObj) == false)
					{
						allAreRemote = false;
						
						break;
					}
				}
				
				if (allAreRemote == false)
				{
					continue;
				}
				
				
				for (String basedObj: basedRemoteObjColl)
				{
					if (Utilities.isOutObj(basedObj) == false)
					{
						continue;
					}
					
					
					Collection<String> basedProjColl = 
						(Collection<String>) remoteObj2ProjMap.get(basedObj);
					if (basedProjColl == null)
					{
						continue;
					}
					
					for (String basedProj: basedProjColl)
					{
						Collection<String> projColl = 
							(Collection<String>) remoteObj2ProjMap.get(obj);
						if (projColl == null)
						{
							continue;
						}
						
						for (String proj: projColl)
						{
							Collection<String> coll = (Collection<String>) proj2ProjMap.get(proj);
							if (coll == null || coll.contains(basedProj) == false)
							{
								if (proj.equals(basedProj) == false)
								{
									proj2ProjMap.put(proj, basedProj);
									String projPair = proj  + " , " + basedProj;
									fre.put(projPair, 1);
									
								}
							}
							else if (coll != null && coll.contains(basedProj) == true)
							{
								if (proj.equals(basedProj) == false)
								{
									String projPair = proj  + " , " + basedProj;
									fre.put(projPair, 1);
								}
							}
								
						}
					}
				}
			}
		}
		
		System.out.println(proj2ProjMap.size());
		
		
		Utilities.writeMultiMapToFile(proj2ProjMap, proj2ProjMapFile);
		
		 FileWriter fstream = new FileWriter(basedFreMapFile);
		 BufferedWriter out = new BufferedWriter(fstream);
		
		for(String projPair: (Set<String>) fre.keySet())
		{
			Collection<Integer> coll = (Collection<Integer>) fre.get(projPair);
			
			out.write(projPair + ", " + coll.size() + "\n");
		}
		
		out.close();
	}
	
	public static void main(String args[]) throws IOException
	{
		GetDependency fuyun = new GetDependency();
		fuyun.getdependency("data/android-4.0.1/obj2ObjMap.csv", 
				"data/android-4.0.1/proj2ProjMap.csv",
				"data/android-4.0.1/remoteObj2ProjMap.csv",
				"data/android-4.0.1/localObj2ProjMap.csv",
				"data/android-4.0.1/basedFreMap.csv");
	}
	
}
